#include "StdAfx.h"
#include "FileHelper.h"

#if !defined(H_OS_VER_WIN9X) && defined(WIN32)


#include <windows.h>
#include <fstream>
#include <dbghelp.h>
#include "CrashMgr.h"
#include <time.h>
#include <stdio.h>
#include <tchar.h>
#include <Tlhelp32.h>

#include "stdafx.h"

#pragma comment(lib, "Dbghelp")
#pragma comment(lib, "version.lib")  // for "VerQueryValue"
#pragma warning(disable: 4996 4244 4748)

using namespace std;

#ifndef INVALID_FILE_ATTRIBUTES
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#endif  

#define USED_CONTEXT_FLAGS CONTEXT_FULL

class StackWalker;
class StackWalkerInternal
{
public:
  StackWalkerInternal(StackWalker *parent, HANDLE hProcess)
  {
    m_parent = parent;
    m_hDbhHelp = NULL;
    pSC = NULL;
    m_hProcess = hProcess;
    m_szSymPath = NULL;
    pSFTA = NULL;
    pSGLFA = NULL;
    pSGMB = NULL;
    pSGMI = NULL;
    pSGO = NULL;
    pSGSFA = NULL;
    pSI = NULL;
    pSLM = NULL;
    pSSO = NULL;
    pSW = NULL;
    pUDSN = NULL;
    pSGSP = NULL;
  }
  ~StackWalkerInternal()
  {
    if (pSC != NULL)
      pSC(m_hProcess);
    if (m_hDbhHelp != NULL)
      FreeLibrary(m_hDbhHelp);
    m_hDbhHelp = NULL;
    m_parent = NULL;
    if(m_szSymPath != NULL)
      free(m_szSymPath);
    m_szSymPath = NULL;
  }
  BOOL Init(LPCSTR szSymPath)
  {
    if (m_parent == NULL)
      return FALSE;
    TCHAR szTemp[4096];
    if (GetModuleFileName(NULL, szTemp, 4096) > 0)
    {
      _tcscat_s(szTemp, _T(".local"));
      if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES)
      {
        if (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)
        {
          _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll"));
          if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
          {
            m_hDbhHelp = LoadLibrary(szTemp);
          }
        }
        if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
        {
          _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));
          if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
          {
            m_hDbhHelp = LoadLibrary(szTemp);
          }
        }
      }
    }
    if (m_hDbhHelp == NULL)
      m_hDbhHelp = LoadLibrary( _T("dbghelp.dll") );
    if (m_hDbhHelp == NULL)
      return FALSE;
    pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" );
    pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" );

    pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" );
    pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" );
    pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" );

    pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" );
    pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" );
    pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" );
    pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );
    pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" );
    pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" );
    pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" );
    pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" );

    if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
      pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
      pSW == NULL || pUDSN == NULL || pSLM == NULL )
    {
      FreeLibrary(m_hDbhHelp);
      m_hDbhHelp = NULL;
      pSC = NULL;
      return FALSE;
    }

    if (szSymPath != NULL)
      m_szSymPath = _strdup(szSymPath);
    if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE)
      this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
      
    DWORD symOptions = this->pSGO();
    symOptions |= SYMOPT_LOAD_LINES;
    symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
    symOptions = this->pSSO(symOptions);

    char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
    if (this->pSGSP != NULL)
    {
      if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE)
        this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
    }
    char szUserName[1024] = {0};
    DWORD dwSize = 1024;
    GetUserNameA(szUserName, &dwSize);
    this->m_parent->OnSymInit(buf, symOptions, szUserName);

    return TRUE;
  }

  StackWalker *m_parent;

  HMODULE m_hDbhHelp;
  HANDLE m_hProcess;
  LPSTR m_szSymPath;

typedef struct IMAGEHLP_MODULE64_V2 {
    DWORD    SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)
    DWORD64  BaseOfImage;            // base load address of module
    DWORD    ImageSize;              // virtual size of the loaded module
    DWORD    TimeDateStamp;          // date/time stamp from pe header
    DWORD    CheckSum;               // checksum from the pe header
    DWORD    NumSyms;                // number of symbols in the symbol table
    SYM_TYPE SymType;                // type of symbols loaded
    CHAR     ModuleName[32];         // module name
    CHAR     ImageName[256];         // image name
    CHAR     LoadedImageName[256];   // symbol file name
};


  // SymCleanup()
  typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
  tSC pSC;

  // SymFunctionTableAccess64()
  typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );
  tSFTA pSFTA;

  // SymGetLineFromAddr64()
  typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
    OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
  tSGLFA pSGLFA;

  // SymGetModuleBase64()
  typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );
  tSGMB pSGMB;

  // SymGetModuleInfo64()
  typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V2 *ModuleInfo );
  tSGMI pSGMI;

  // SymGetOptions()
  typedef DWORD (__stdcall *tSGO)( VOID );
  tSGO pSGO;

  // SymGetSymFromAddr64()
  typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
    OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
  tSGSFA pSGSFA;

  // SymInitialize()
  typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
  tSI pSI;

  // SymLoadModule64()
  typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,
    IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
  tSLM pSLM;

  // SymSetOptions()
  typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
  tSSO pSSO;

  // StackWalk64()
  typedef BOOL (__stdcall *tSW)( 
    DWORD MachineType, 
    HANDLE hProcess,
    HANDLE hThread, 
    LPSTACKFRAME64 StackFrame, 
    PVOID ContextRecord,
    PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
    PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
    PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
    PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
  tSW pSW;

  // UnDecorateSymbolName()
  typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,
    DWORD UndecoratedLength, DWORD Flags );
  tUDSN pUDSN;

  typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);
  tSGSP pSGSP;


private:
  // **************************************** ToolHelp32 ************************
  #define MAX_MODULE_NAME32 255
  #define TH32CS_SNAPMODULE   0x00000008
  #pragma pack( push, 8 )
  typedef struct tagMODULEENTRY32
  {
      DWORD   dwSize;
      DWORD   th32ModuleID;
      DWORD   th32ProcessID;
      DWORD   GlblcntUsage;
      DWORD   ProccntUsage;
      BYTE  * modBaseAddr; 
      DWORD   modBaseSize;
      HMODULE hModule;
      char    szModule[MAX_MODULE_NAME32 + 1];
      char    szExePath[MAX_PATH];
  } MODULEENTRY32;
  typedef MODULEENTRY32 *  PMODULEENTRY32;
  typedef MODULEENTRY32 *  LPMODULEENTRY32;
  #pragma pack( pop )

  BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid)
  {
    // CreateToolhelp32Snapshot()
    typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
    // Module32First()
    typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
    // Module32Next()
    typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);

    const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
    HINSTANCE hToolhelp = NULL;
    tCT32S pCT32S = NULL;
    tM32F pM32F = NULL;
    tM32N pM32N = NULL;

    HANDLE hSnap;
    MODULEENTRY32 me;
    me.dwSize = sizeof(me);
    BOOL keepGoing;
    size_t i;

    for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ )
    {
      hToolhelp = LoadLibrary( dllname[i] );
      if (hToolhelp == NULL)
        continue;
      pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
      pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
      pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
      if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) )
        break; // found the functions!
      FreeLibrary(hToolhelp);
      hToolhelp = NULL;
    }

    if (hToolhelp == NULL)
      return FALSE;

    hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
    if (hSnap == (HANDLE) -1)
      return FALSE;

    keepGoing = !!pM32F( hSnap, &me );
    int cnt = 0;
    while (keepGoing)
    {
      this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize);
      cnt++;
      keepGoing = !!pM32N( hSnap, &me );
    }
    CloseHandle(hSnap);
    FreeLibrary(hToolhelp);
    if (cnt <= 0)
      return FALSE;
    return TRUE;
  }  // GetModuleListTH32

  // **************************************** PSAPI ************************
  typedef struct _MODULEINFO {
      LPVOID lpBaseOfDll;
      DWORD SizeOfImage;
      LPVOID EntryPoint;
  } MODULEINFO, *LPMODULEINFO;

  BOOL GetModuleListPSAPI(HANDLE hProcess)
  {
    // EnumProcessModules()
    typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
    // GetModuleFileNameEx()
    typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
    // GetModuleBaseName()
    typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
    // GetModuleInformation()
    typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );

    HINSTANCE hPsapi;
    tEPM pEPM;
    tGMFNE pGMFNE;
    tGMBN pGMBN;
    tGMI pGMI;

    DWORD i;
    DWORD cbNeeded;
    MODULEINFO mi;
    HMODULE *hMods = 0;
    char *tt = NULL;
    char *tt2 = NULL;
    const SIZE_T TTBUFLEN = 8096;
    int cnt = 0;

    hPsapi = LoadLibrary( _T("psapi.dll") );
    if (hPsapi == NULL)
      return FALSE;

    pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
    pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
    pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
    pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
    if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) )
    {
      FreeLibrary(hPsapi);
      return FALSE;
    }

    hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
    tt = (char*) malloc(sizeof(char) * TTBUFLEN);
    tt2 = (char*) malloc(sizeof(char) * TTBUFLEN);
    if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) )
      goto cleanup;

    if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
    {
      goto cleanup;
    }

    if ( cbNeeded > TTBUFLEN )
    {
      goto cleanup;
    }

    for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
    {
      pGMI(hProcess, hMods[i], &mi, sizeof mi );
      tt[0] = 0;
      pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );
      tt2[0] = 0;
      pGMBN(hProcess, hMods[i], tt2, TTBUFLEN );

      DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage);
      if (dwRes != ERROR_SUCCESS)
        this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);
      cnt++;
    }

  cleanup:
    if (hPsapi != NULL) FreeLibrary(hPsapi);
    if (tt2 != NULL) free(tt2);
    if (tt != NULL) free(tt);
    if (hMods != NULL) free(hMods);

    return cnt != 0;
  }  // GetModuleListPSAPI

  DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)
  {
    CHAR *szImg = _strdup(img);
    CHAR *szMod = _strdup(mod);
    DWORD result = ERROR_SUCCESS;
    if ( (szImg == NULL) || (szMod == NULL) )
      result = ERROR_NOT_ENOUGH_MEMORY;
    else
    {
      if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
        result = GetLastError();
    }
    ULONGLONG fileVersion = 0;
    if ( (m_parent != NULL) && (szImg != NULL) )
    {
      if ( (this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0)
      {
        VS_FIXEDFILEINFO *fInfo = NULL;
        DWORD dwHandle;
        DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
        if (dwSize > 0)
        {
          LPVOID vData = malloc(dwSize);
          if (vData != NULL)
          {
            if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)
            {
              UINT len;
              TCHAR szSubBlock[] = _T("\\");
              if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0)
                fInfo = NULL;
              else
              {
                fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);
              }
            }
            free(vData);
          }
        }
      }
      IMAGEHLP_MODULE64_V2 Module;
      const char *szSymType = "-unknown-";
      if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)
      {
        switch(Module.SymType)
        {
          case SymNone:
            szSymType = "-nosymbols-";
            break;
          case SymCoff:
            szSymType = "COFF";
            break;
          case SymCv:
            szSymType = "CV";
            break;
          case SymPdb:
            szSymType = "PDB";
            break;
          case SymExport:
            szSymType = "-exported-";
            break;
          case SymDeferred:
            szSymType = "-deferred-";
            break;
          case SymSym:
            szSymType = "SYM";
            break;
          case 8: //SymVirtual:
            szSymType = "Virtual";
            break;
          case 9: // SymDia:
            szSymType = "DIA";
            break;
        }
      }
      this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion);
    }
    if (szImg != NULL) free(szImg);
    if (szMod != NULL) free(szMod);
    return result;
  }
public:
  BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId)
  {
    // first try toolhelp32
    if (GetModuleListTH32(hProcess, dwProcessId))
      return true;
    // then try psapi
    return GetModuleListPSAPI(hProcess);
  }


  BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V2 *pModuleInfo)
  {
    if(this->pSGMI == NULL)
    {
      SetLastError(ERROR_DLL_INIT_FAILED);
      return FALSE;
    }
    pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
    void *pData = malloc(4096);
    if (pData == NULL)
    {
      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
      return FALSE;
    }
    memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));
    if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V2*) pData) != FALSE)
    {
      memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));
      pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
      free(pData);
      return TRUE;
    }
    free(pData);
    SetLastError(ERROR_DLL_INIT_FAILED);
    return FALSE;
  }
};

StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess)
{
	this->m_options = OptionsAll;
	this->m_modulesLoaded = FALSE;
	this->m_hProcess = hProcess;
	this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
	this->m_dwProcessId = dwProcessId;
	this->m_szSymPath = NULL;
}
StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)
{
	this->m_options = options;
	this->m_modulesLoaded = FALSE;
	this->m_hProcess = hProcess;
	this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
	this->m_dwProcessId = dwProcessId;
	if (szSymPath != NULL)
	{
		this->m_szSymPath = _strdup(szSymPath);
		this->m_options |= SymBuildPath;
	}
	else
		this->m_szSymPath = NULL;
}

StackWalker::~StackWalker()
{
	if (m_szSymPath != NULL)
		free(m_szSymPath);
	m_szSymPath = NULL;
	if (this->m_sw != NULL)
		delete this->m_sw;
	this->m_sw = NULL;
}

BOOL StackWalker::LoadModules()
{
	if (this->m_sw == NULL)
	{
		SetLastError(ERROR_DLL_INIT_FAILED);
		return FALSE;
	}
	if (m_modulesLoaded != FALSE)
		return TRUE;

	char *szSymPath = NULL;
	if ( (this->m_options & SymBuildPath) != 0)
	{
		const size_t nSymPathLen = 4096;
		szSymPath = (char*) malloc(nSymPathLen);
		if (szSymPath == NULL)
		{
			SetLastError(ERROR_NOT_ENOUGH_MEMORY);
			return FALSE;
		}
		szSymPath[0] = 0;
		if (this->m_szSymPath != NULL)
		{
			strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);
			strcat_s(szSymPath, nSymPathLen, ";");
		}

		strcat_s(szSymPath, nSymPathLen, ".;");

		const size_t nTempLen = 1024;
		char szTemp[nTempLen];
		// Now add the current directory:
		if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
		{
			szTemp[nTempLen-1] = 0;
			strcat_s(szSymPath, nSymPathLen, szTemp);
			strcat_s(szSymPath, nSymPathLen, ";");
		}

		// Now add the path for the main-module:
		if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)
		{
			szTemp[nTempLen-1] = 0;
			for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p)
			{
				// locate the rightmost path separator
				if ( (*p == '\\') || (*p == '/') || (*p == ':') )
				{
					*p = 0;
					break;
				}
			}  // for (search for path separator...)
			if (strlen(szTemp) > 0)
			{
				strcat_s(szSymPath, nSymPathLen, szTemp);
				strcat_s(szSymPath, nSymPathLen, ";");
			}
		}
		if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
		{
			szTemp[nTempLen-1] = 0;
			strcat_s(szSymPath, nSymPathLen, szTemp);
			strcat_s(szSymPath, nSymPathLen, ";");
		}
		if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
		{
			szTemp[nTempLen-1] = 0;
			strcat_s(szSymPath, nSymPathLen, szTemp);
			strcat_s(szSymPath, nSymPathLen, ";");
		}
		if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
		{
			szTemp[nTempLen-1] = 0;
			strcat_s(szSymPath, nSymPathLen, szTemp);
			strcat_s(szSymPath, nSymPathLen, ";");
			// also add the "system32"-directory:
			strcat_s(szTemp, nTempLen, "\\system32");
			strcat_s(szSymPath, nSymPathLen, szTemp);
			strcat_s(szSymPath, nSymPathLen, ";");
		}

		if ( (this->m_options & SymBuildPath) != 0)
		{
			if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)
			{
				szTemp[nTempLen-1] = 0;
				strcat_s(szSymPath, nSymPathLen, "SRV*");
				strcat_s(szSymPath, nSymPathLen, szTemp);
				strcat_s(szSymPath, nSymPathLen, "\\websymbols");
				strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");
			}
			else
				strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
		}
	}

	// First Init the whole stuff...
	BOOL bRet = this->m_sw->Init(szSymPath);
	if (szSymPath != NULL) free(szSymPath); szSymPath = NULL;
	if (bRet == FALSE)
	{
		this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
		SetLastError(ERROR_DLL_INIT_FAILED);
		return FALSE;
	}

	bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId);
	if (bRet != FALSE)
		m_modulesLoaded = TRUE;
	return bRet;
}

static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL;
static LPVOID s_readMemoryFunction_UserData = NULL;

BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData)
{
	CONTEXT c;;
	CallstackEntry csEntry;
	IMAGEHLP_SYMBOL64 *pSym = NULL;
	StackWalkerInternal::IMAGEHLP_MODULE64_V2 Module;
	IMAGEHLP_LINE64 Line;
	int frameNum;

	if (m_modulesLoaded == FALSE)
		this->LoadModules();
	if (this->m_sw->m_hDbhHelp == NULL)
	{
		SetLastError(ERROR_DLL_INIT_FAILED);
		return FALSE;
	}

	s_readMemoryFunction = readMemoryFunction;
	s_readMemoryFunction_UserData = pUserData;

	if (context == NULL)
	{
		if (hThread == GetCurrentThread())
		{
			GET_CURRENT_CONTEXT(c, USED_CONTEXT_FLAGS);
		}
		else
		{
			SuspendThread(hThread);
			memset(&c, 0, sizeof(CONTEXT));
			c.ContextFlags = USED_CONTEXT_FLAGS;
			if (GetThreadContext(hThread, &c) == FALSE)
			{
				ResumeThread(hThread);
				return FALSE;
			}
		}
	}
	else
		c = *context;

	STACKFRAME64 s;
	memset(&s, 0, sizeof(s));
	DWORD imageType;
#ifdef _M_IX86
	imageType = IMAGE_FILE_MACHINE_I386;
	s.AddrPC.Offset = c.Eip;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.Ebp;
	s.AddrFrame.Mode = AddrModeFlat;
	s.AddrStack.Offset = c.Esp;
	s.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
	imageType = IMAGE_FILE_MACHINE_AMD64;
	s.AddrPC.Offset = c.Rip;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.Rsp;
	s.AddrFrame.Mode = AddrModeFlat;
	s.AddrStack.Offset = c.Rsp;
	s.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
	imageType = IMAGE_FILE_MACHINE_IA64;
	s.AddrPC.Offset = c.StIIP;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.IntSp;
	s.AddrFrame.Mode = AddrModeFlat;
	s.AddrBStore.Offset = c.RsBSP;
	s.AddrBStore.Mode = AddrModeFlat;
	s.AddrStack.Offset = c.IntSp;
	s.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif

	pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
	if (!pSym) goto cleanup;  // not enough memory...
	memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
	pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
	pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;

	memset(&Line, 0, sizeof(Line));
	Line.SizeOfStruct = sizeof(Line);

	memset(&Module, 0, sizeof(Module));
	Module.SizeOfStruct = sizeof(Module);

	for (frameNum = 0; ; ++frameNum )
	{
		if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) )
		{
			this->OnDbgHelpErr("StackWalk64", GetLastError(), s.AddrPC.Offset);
			break;
		}

		csEntry.offset = s.AddrPC.Offset;
		csEntry.name[0] = 0;
		csEntry.undName[0] = 0;
		csEntry.undFullName[0] = 0;
		csEntry.offsetFromSmybol = 0;
		csEntry.offsetFromLine = 0;
		csEntry.lineFileName[0] = 0;
		csEntry.lineNumber = 0;
		csEntry.loadedImageName[0] = 0;
		csEntry.moduleName[0] = 0;
		if (s.AddrPC.Offset == s.AddrReturn.Offset)
		{
			this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
			break;
		}
		if (s.AddrPC.Offset != 0)
		{
			if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE)
			{
				strcpy_s(csEntry.name, pSym->Name);
				this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY );
				this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE );
			}
			else
			{
				this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
			}

			if (this->m_sw->pSGLFA != NULL )
			{
				if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE)
				{
					csEntry.lineNumber = Line.LineNumber;
					strcpy_s(csEntry.lineFileName, Line.FileName);
				}
				else
				{
					this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
				}
			}

			if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE)
			{
				switch ( Module.SymType )
				{
				case SymNone:
					csEntry.symTypeString = "-nosymbols-";
					break;
				case SymCoff:
					csEntry.symTypeString = "COFF";
					break;
				case SymCv:
					csEntry.symTypeString = "CV";
					break;
				case SymPdb:
					csEntry.symTypeString = "PDB";
					break;
				case SymExport:
					csEntry.symTypeString = "-exported-";
					break;
				case SymDeferred:
					csEntry.symTypeString = "-deferred-";
					break;
				case SymSym:
					csEntry.symTypeString = "SYM";
					break;
#if API_VERSION_NUMBER >= 9
				case SymDia:
					csEntry.symTypeString = "DIA";
					break;
#endif
				case 8: //SymVirtual:
					csEntry.symTypeString = "Virtual";
					break;
				default:
					csEntry.symTypeString = NULL;
					break;
				}

				strcpy_s(csEntry.moduleName, Module.ModuleName);
				csEntry.baseOfImage = Module.BaseOfImage;
				strcpy_s(csEntry.loadedImageName, Module.LoadedImageName);
			}
			else
			{
				this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
			}
		}

		CallstackEntryType et = nextEntry;
		if (frameNum == 0)
			et = firstEntry;
		this->OnCallstackEntry(et, csEntry);

		if (s.AddrReturn.Offset == 0)
		{
			this->OnCallstackEntry(lastEntry, csEntry);
			SetLastError(ERROR_SUCCESS);
			break;
		}
	}

cleanup:
	if (pSym) free( pSym );

	if (context == NULL)
		ResumeThread(hThread);

	return TRUE;
}

BOOL __stdcall StackWalker::myReadProcMem(
	HANDLE      hProcess,
	DWORD64     qwBaseAddress,
	PVOID       lpBuffer,
	DWORD       nSize,
	LPDWORD     lpNumberOfBytesRead
	)
{
	if (s_readMemoryFunction == NULL)
	{
		SIZE_T st;
		BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st);
		*lpNumberOfBytesRead = (DWORD) st;
		return bRet;
	}
	else
	{
		return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData);
	}
}

void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
{
	CHAR buffer[STACKWALK_MAX_NAMELEN];
	if (fileVersion == 0)
		_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName);
	else
	{
		DWORD v4 = (DWORD) fileVersion & 0xFFFF;
		DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF;
		DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF;
		DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF;
		_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);
	}
}

void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
{
	CHAR buffer[STACKWALK_MAX_NAMELEN];
	if ( (eType != lastEntry) && (entry.offset != 0) )
	{
		if (entry.name[0] == 0)
			strcpy_s(entry.name, "(function-name not available)");
		if (entry.undName[0] != 0)
			strcpy_s(entry.name, entry.undName);
		if (entry.undFullName[0] != 0)
			strcpy_s(entry.name, entry.undFullName);
		if (entry.lineFileName[0] == 0)
		{
			strcpy_s(entry.lineFileName, "(filename not available)");
			if (entry.moduleName[0] == 0)
				strcpy_s(entry.moduleName, "(module-name not available)");
			_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) (entry.offset-entry.baseOfImage), entry.moduleName, entry.lineFileName, entry.name);
		}
		else
			_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s (%d): %s\n", (LPVOID) (entry.offset-entry.baseOfImage), entry.moduleName,entry.lineFileName, entry.lineNumber, entry.name);
		OnOutput(buffer);
	}
}

void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
{
	CHAR buffer[STACKWALK_MAX_NAMELEN];
	_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr);
}

void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
{
	CHAR buffer[STACKWALK_MAX_NAMELEN];
	_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName);
	OSVERSIONINFOEXA ver;
	ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));
	ver.dwOSVersionInfoSize = sizeof(ver);
	if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE)
	{
		_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", 
			ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
			ver.szCSDVersion, ver.wSuiteMask, ver.wProductType);
		OnOutput(buffer);
	}
}

void StackWalker::OnOutput(LPCSTR buffer)
{
	OutputDebugStringA(buffer);
}

class MyStackWalker : public StackWalker
{
public:
	MyStackWalker(const char* file_name) : StackWalker() { fs.open(file_name, ios::out|ios::trunc) ;}
	MyStackWalker(DWORD dwProcessId, HANDLE hProcess) : StackWalker(dwProcessId, hProcess) {}
	virtual void OnOutput(LPCSTR szText) { printf(szText); StackWalker::OnOutput(szText); fs<<szText;}
	fstream fs;
};

void MyExceptionHandler_other(_EXCEPTION_POINTERS* pExp,const char* file_name)
{
	MyStackWalker sw(file_name);
	sw.ShowCallstack(GetCurrentThread(), pExp->ContextRecord);

	g_pMainWnd->ProcessCrash();
	if (CrashMgr::ins().pCallBack)
	{
		CrashMgr::ins().pCallBack->ProcessCrash();
	}
	if (!CrashMgr::ins().mb_log_to_file)
	{
		return;
	}
}

LONG __stdcall MyExceptionHandler( _EXCEPTION_POINTERS* ExceptInfo )
{
	::OutputDebugStringA("Enter Crash Mgr...");

	char buf_crashdir[MAX_PATH];
	char buf_exe_short[100] = {0};
	char buf_exe_path[MAX_PATH] = {0};
	char buf_exe[MAX_PATH] = {0};
	::GetModuleFileNameA(NULL, buf_exe, sizeof(buf_exe) / sizeof(char));
	int i_buf_exe_len = strlen(buf_exe);
	for (int i = i_buf_exe_len - 1; i >= 0; i-- )
	{
		if (buf_exe[i] == '\\' || buf_exe[i] == '/')
		{
			strcpy(buf_exe_short, buf_exe + i + 1 );
			strncpy(buf_exe_path, buf_exe, i);
			sprintf(buf_crashdir, "%s\\%s", buf_exe_path, "CrashDump");
			break;
		}
	}
	if (buf_exe_short[0] == 0)
	{
		strcpy(buf_exe_short, "UnknownExe" );
	}
	DWORD dw_pid = GetCurrentProcessId();
	CreateDirectoryA(buf_crashdir, NULL);

	char szDate[64] = { 0 };
	struct tm t;
	time_t t1 = time(nullptr);
	localtime_s(&t, &t1);
	strftime(szDate, 64, "%Y.%m.%d.%H.%M.%S", &t);

	char c_dump_file[MAX_PATH] = {0};
	sprintf(c_dump_file, "%s\\CrashDump\\Crash-%s-%u.dmp", buf_exe_path, szDate, dw_pid);

	char c_dump_context[MAX_PATH] = {0};
	sprintf(c_dump_context, "%s\\CrashDump\\Crash-%s-%u.txt", buf_exe_path, szDate, dw_pid);

	char c_dbg_text[1024] = {0};
	__try 
	{
		HANDLE hFile = CreateFileA( c_dump_file,
			GENERIC_WRITE,
			FILE_SHARE_READ,
			NULL,
			OPEN_ALWAYS,
			0,
			NULL );

		if ( hFile == INVALID_HANDLE_VALUE ) 
		{
			DWORD dw_err = GetLastError();

			sprintf(c_dbg_text, "CreateFile %s fail, error code %d\n", c_dump_file, dw_err);
			::OutputDebugStringA(c_dbg_text);
			MyExceptionHandler_other(ExceptInfo,c_dump_context);
		}
		else
		{
			MINIDUMP_EXCEPTION_INFORMATION miniExcept;
			miniExcept.ExceptionPointers = ExceptInfo;
			miniExcept.ClientPointers = TRUE;
			miniExcept.ThreadId = GetCurrentThreadId();

			sprintf(c_dbg_text, "Start to dump %s\n", c_dump_file);
			::OutputDebugStringA(c_dbg_text);

			BOOL b = MiniDumpWriteDump( GetCurrentProcess(),
				GetCurrentProcessId(),
				hFile,
				MiniDumpNormal,
				&miniExcept,
				NULL,
				NULL );

			if ( !b )
			{
				sprintf(c_dbg_text, "Dump fail: %s\n", c_dump_file);
				::OutputDebugStringA(c_dbg_text);
			}
			else
			{
				sprintf(c_dbg_text, "Dump ok: %s\n", c_dump_file);
				::OutputDebugStringA(c_dbg_text);
			}

			CloseHandle( hFile );
			MyExceptionHandler_other(ExceptInfo,c_dump_context);
		}
	}
	__except ( MyExceptionHandler( GetExceptionInformation() ) ) 
	{
	}
	return EXCEPTION_CONTINUE_SEARCH/*EXCEPTION_EXECUTE_HANDLER*/;
}

CrashMgr::CrashMgr(void)
{
	pCallBack = 0;

	mb_log_to_file = false;

	SetInvalidHandle();
//#ifndef _DEBUG
	m_preFilter = SetUnhandledExceptionFilter(MyExceptionHandler);
//#endif
}

CrashMgr::~CrashMgr(void)
{
}

void CrashMgr::MyPureCallHandler()
{
	throw std::invalid_argument("");
}

void CrashMgr::MyInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved)
{
	throw std::invalid_argument("");
}

void CrashMgr::SetInvalidHandle()
{
	m_preIph = _set_invalid_parameter_handler(MyInvalidParameterHandler);
	m_prePch = _set_purecall_handler(MyPureCallHandler);   //At application, this call can stop show the error message box.
}

void CrashMgr::UnSetInvalidHandle()
{
	if (m_preIph)_set_invalid_parameter_handler(m_preIph);
	if (m_prePch)_set_purecall_handler(m_prePch); //At application this can stop show the error message box.
}

int CrashMgr::set_callback(CrashMgrCallBack* _pCallBack)
{
	pCallBack = _pCallBack;
	return 0;
}

CrashMgr CrashMgr::sta_ins;
CrashMgr& CrashMgr::ins()
{
	return sta_ins;
}


#endif

